package com.burst.reptile.framework.security.service;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSONObject;
import com.burst.reptile.common.constant.Constants;
import com.burst.reptile.common.utils.ServletUtils;
import com.burst.reptile.common.utils.ToolUtil;
import com.burst.reptile.common.utils.ip.AddressUtils;
import com.burst.reptile.common.utils.ip.IpUtils;
import com.burst.reptile.framework.constant.ConstantFactory;
import com.burst.reptile.framework.redis.RedisCache;
import com.burst.reptile.framework.security.LoginUser;
import com.burst.reptile.modular.system.entity.SysUser;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;

/**
 * token验证处理
 * 
 * @author wujin
 * @date: 2020年5月21日 下午3:03:55
 */
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class TokenService {

	// 令牌自定义标识
	@Value("${token.header}")
	private String header;

	// 令牌秘钥
	@Value("${token.secret}")
	private String secret;

	// 令牌有效期（默认30分钟）
	@Value("${token.expireTime}")
	private int expireTime;

	protected static final long MILLIS_SECOND = 1000;

	protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;

	private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;

	private final RedisCache redisCache;

	/**
	 * 获取用户身份信息
	 * 
	 * @return 用户信息
	 */
	public LoginUser getLoginUser(HttpServletRequest request) {
		// 获取请求携带的令牌
		String token = getToken(request);
		if (StrUtil.isNotEmpty(token)) {
			Claims claims = parseToken(token);
			// 解析对应的权限以及用户信息
			String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
			String userKey = getTokenKey(uuid);
			Object cacheObject = redisCache.getCacheObject(userKey);
			if (ToolUtil.isNotEmpty(cacheObject)) {
				LoginUser user = new LoginUser();
				BeanUtil.copyProperties(cacheObject, user);
				return user;
			}
		}
		return null;
	}

	/**
	 * 设置用户身份信息
	 */
	public void setLoginUser(LoginUser loginUser) {
		if (ToolUtil.isNotEmpty(loginUser) && StrUtil.isNotEmpty(loginUser.getToken())) {
			refreshToken(loginUser);
		}
	}

	/**
	 * 删除用户身份信息
	 */
	public void delLoginUser(String token) {
		if (StrUtil.isNotEmpty(token)) {
			String userKey = getTokenKey(token);
			redisCache.deleteObject(userKey);
		}
	}

	/**
	 * 创建令牌
	 * 
	 * @param loginUser 用户信息
	 * @return 令牌
	 */
	public String createToken(LoginUser loginUser) {
		String token = IdUtil.objectId();
		loginUser.setToken(token);
		setUserAgent(loginUser);
		refreshToken(loginUser);
		Map<String, Object> claims = new HashMap<>();
		claims.put(Constants.LOGIN_USER_KEY, token);
		return createToken(claims);
	}

	/**
	 * 验证令牌有效期，相差不足20分钟，自动刷新缓存
	 * 
	 * @param token 令牌
	 * @return 令牌
	 */
	public void verifyToken(LoginUser loginUser) {
		Long expireTime = loginUser.getExpireTime();
		if (ToolUtil.isNotEmpty(expireTime)) {
			long currentTime = System.currentTimeMillis();
			if (expireTime - currentTime <= MILLIS_MINUTE_TEN) {
				refreshToken(loginUser);
			}
		}
	}

	/**
	 * 刷新令牌有效期
	 * 
	 * @param loginUser 登录信息
	 */
	public void refreshToken(LoginUser loginUser) {
		loginUser.setLoginTime(System.currentTimeMillis());
		loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
		// 根据uuid将loginUser缓存
		String userKey = getTokenKey(loginUser.getToken());
		redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
	}

	/**
	 * 设置用户代理信息
	 * 
	 * @param loginUser 登录信息
	 */
	public void setUserAgent(LoginUser loginUser) {
		UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
		String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
		loginUser.setIpaddr(ip);
		loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
		loginUser.setBrowser(userAgent.getBrowser().getName());
		loginUser.setOs(userAgent.getOs().getName());
	}

	/**
	 * 从数据声明生成令牌
	 *
	 * @param claims 数据声明
	 * @return 令牌
	 */
	private String createToken(Map<String, Object> claims) {
		String token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact();
		return token;
	}

	/**
	 * 从令牌中获取数据声明
	 *
	 * @param token 令牌
	 * @return 数据声明
	 */
	private Claims parseToken(String token) {
		return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
	}

	/**
	 * 从令牌中获取用户名
	 *
	 * @param token 令牌
	 * @return 用户名
	 */
	public String getUsernameFromToken(String token) {
		Claims claims = parseToken(token);
		return claims.getSubject();
	}

	/**
	 * 获取城市站点
	 * 
	 * @return
	 */
	public String getCitySite() {
		HttpServletRequest request = ServletUtils.getRequest();
		String code = request.getHeader(Constants.SITE);
		if (StrUtil.isNotEmpty(code)) {
			return code;
		}
		Cookie[] cookies = request.getCookies();
		if (cookies != null) {
			for (Cookie cookie : cookies) {
				if (Constants.CITY_SITE.equals(cookie.getName())) {
					code = cookie.getValue();
				}
			}
		}
		return StrUtil.isNotBlank(code) ? code : "500100";
	}

	/**
	 * 获取请求token
	 *
	 * @param request
	 * @return token
	 */
	private String getToken(HttpServletRequest request) {
		String token = request.getHeader(header); // 从header中获取token
		if (ToolUtil.isEmpty(token)) { // 从cookie中获取token
			Cookie[] cookies = request.getCookies();
			if (cookies != null) {
				for (Cookie cookie : cookies) {
					if (Constants.COOKIE_AUTH.equals(cookie.getName())) {
						token = cookie.getValue();
						break;
					}
				}
			}
		}
		if (StrUtil.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {
			token = token.replace(Constants.TOKEN_PREFIX, "");
		}
		return token;
	}

	/**
	 * 登录缓存redis key
	 * 
	 * @param uuid
	 * @return
	 */
	private String getTokenKey(String uuid) {
		return Constants.LOGIN_TOKEN_KEY + uuid;
	}

	/**
	 * 设置Cookie
	 * 
	 * @param token
	 */
	public void addLoginCookie(String token) {
		// 创建cookie
		Cookie authorization = new Cookie(Constants.COOKIE_AUTH, token);
		authorization.setMaxAge(expireTime * 60);
		authorization.setHttpOnly(true);
		authorization.setPath("/");
		HttpServletResponse response = ServletUtils.getResponse();
		response.addCookie(authorization);
	}

	/**
	 * 获取微信用户信息
	 * 
	 * @return
	 */
	public LoginUser getWxUser() {
		HttpServletRequest request = ServletUtils.getRequest();
		String token = request.getHeader(Constants.TOKEN);
		if (StrUtil.isNotEmpty(token)) {
			Object object = redisCache.getCacheObject(Constants.LOGIN_TOKEN_KEY + token);
			if (ToolUtil.isNotEmpty(object)) {
				LoginUser loginUser = JSONObject.parseObject(JSONObject.toJSONString(object), LoginUser.class);
				if(ToolUtil.isNotEmpty(loginUser.getUser())) {
					SysUser user = loginUser.getUser();
					Long userId = user.getUserId();
					user = ConstantFactory.me().getUser(userId);
					// 获取粉丝量
					user.setFans(ConstantFactory.me().getFans(userId));
					// 获取关注量
					user.setFollow(ConstantFactory.me().getAttention(userId));
					// 获取点赞量
					user.setLike(ConstantFactory.me().getLikes(userId));
					// 获取获赞量
					user.setBePraised(ConstantFactory.me().getBePraised(userId));
					// 获取发布量
					user.setReleaseVolume(ConstantFactory.me().getReleaseVolume(userId));
					// 收藏量
					user.setCollection(ConstantFactory.me().getCollects(userId));
					// 草稿数量
					user.setDraftsNum(ConstantFactory.me().getDraftsNum(userId));
					// 未读消息数量
					user.setMsgNum(ConstantFactory.me().getMsgNum(userId));
					// 未读私信数量
					user.setPrivateLetterNum(ConstantFactory.me().getPrivateLetterNum(userId));
					
					SysUser dbu = ConstantFactory.me().getUser(userId);
					if(ToolUtil.isNotEmpty(dbu)) {
						user.setAvatar(dbu.getAvatar());
						user.setUserName(dbu.getUserName());
						user.setSex(dbu.getSex());
						user.setNickName(dbu.getNickName());
						user.setRemark(dbu.getRemark());
					}
					loginUser.setUser(user);
					return loginUser;
				}
			}
		}
		return null;
	}

}
